package com.lianqi.emcpframework.dataaccess;

import java.io.Serializable;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import org.springframework.util.StringUtils;

/**
 * @program emcp-framework
 * @ClassName DataAccessContext
 * @description: 附加的数据权限
 * @author: sky
 * @create: 2019/06/01 16:06
 */
public interface DataAccessContext extends Serializable {

    static Optional<DataAccessContext> current() {
        return Optional.ofNullable(DataAccessContextHolder.get());
    }

    List<DataAccess> getDataAccessList();

    Serializable getContextId();

    default List<DataAccess> getDataAccess(String domain) {
        if (StringUtils.isEmpty(domain)) {
            return new ArrayList<>();
        }
        return getDataAccessList().stream().filter(dataAccess -> domain.equalsIgnoreCase(dataAccess.getDomain()))
            .collect(Collectors.toList());
    }

    /**
     * 查找数据权限配置
     *
     * @param predicate
     *            数据权限配置匹配规则
     * @param <T>
     *            数据权限配置类型
     * @return {@link Optional}
     */
    default <T extends DataAccess> Optional<List<T>> findDataAccess(DataAccessPredicate<T> predicate) {
        return Optional.of(getDataAccessList().stream().filter(predicate).collect(() -> new ArrayList<>(),
            (list, dataAccess) -> list.add((T)dataAccess), (list1, list2) -> list1.addAll(list2)));
    }

    /**
     * 查找字段过滤的数据权限配置(列级数据权限),比如:不查询某些字段
     *
     * @param action
     *            权限操作类型 {@link OperationType#query}
     * @return {@link Optional}
     * @see DataAccess
     * @see DataAccess#getFilterFields()
     */
    default Optional<List<DataAccess>> findFieldFilter(String domain, String action) {
        return findDataAccess(access -> domain.equalsIgnoreCase(access.getDomain()) && access.getActions() != null
            && access.getActions().contains(action) && access.getSetFilterType() != null);
    }

    /**
     * 查找数据范围权限控制配置(行级数据权限),比如: 只能查询本机构的数据
     *
     * @param scopeType
     *            范围类型,由具体的实现定义,如: 只能查看自己所在机构
     * @param action
     *            权限操作 {@link OperationType#query}
     * @return 未配置时返回空set, 不会返回null
     */
    default Set<String> findSetScope(String domain, String action, String scopeType) {
        return findSetScope(scope(domain, action, scopeType));
    }

    /**
     * 查找数据范围权限控制配置(行级数据权限),比如: 只能查询本机构的数据
     * 
     * @param predicate
     * @return
     */
    default Set<String> findSetScope(DataAccessContext.DataAccessPredicate<DataAccess> predicate) {
        return findDataAccess(predicate).get().stream().map(DataAccess::getSetScope).collect(() -> new HashSet<>(),
            (set, scope) -> set.addAll(scope), (set1, set2) -> set1.addAll(set2));
    }

    /**
     * 获取需要脱敏处理的字段
     * 
     * @param domain
     *            业务实体对象
     * @param action
     *            权限操作
     * @return 未配置时返回空set, 不会返回null
     */
    default Set<String> findMaskFields(String domain, String action) {
        return findFieldFilter(domain, action).get().stream()
            .filter(access -> FieldFilterType.mask.getValue().equalsIgnoreCase(access.getFieldFilterType()))
            .map(DataAccess::getFilterFields)
            .collect(() -> new HashSet<>(), (set, scope) -> set.addAll(scope), (set1, set2) -> set1.addAll(set2));
    }

    /**
     * 获取不能执行操作的字段
     *
     * @param action
     *            权限操作
     * @return 未配置时返回空set, 不会返回null
     */
    default Set<String> findWhiteListFields(String domain, String action) {
        return findFieldFilter(domain, action).get().stream()
            .filter(access -> FieldFilterType.scope.getValue().equalsIgnoreCase(access.getFieldFilterType()))
            .map(DataAccess::getFilterFields)
            .collect(() -> new HashSet<>(), (set, scope) -> set.addAll(scope), (set1, set2) -> set1.addAll(set2));
    }

    /**
     * 获取不能执行操作的字段
     *
     * @param action
     *            权限操作
     * @return 未配置时返回空set, 不会返回null
     */
    default Set<String> findDenyFields(String domain, String action) {
        return findFieldFilter(domain, action).get().stream()
            .filter(access -> FieldFilterType.deny.getValue().equalsIgnoreCase(access.getFieldFilterType()))
            .map(DataAccess::getFilterFields)
            .collect(() -> new HashSet<>(), (set, scope) -> set.addAll(scope), (set1, set2) -> set1.addAll(set2));
    }

    /**
     * 构造一个数据范围权限控制配置查找逻辑
     *
     * @param scopeType
     *            范围类型,由具体的实现定义,如: 只能查看自己所在机构
     * @param action
     *            权限操作 {@link OperationType#query}
     * @return {@link DataAccessPredicate}
     */
    static DataAccessContext.DataAccessPredicate<DataAccess> scope(String domain, String action, String scopeType) {
        Objects.requireNonNull(domain, "domain can not be null");
        Objects.requireNonNull(action, "action can not be null");
        Objects.requireNonNull(scopeType, "scopeType can not be null");
        return access -> domain.equalsIgnoreCase(access.getDomain()) && access.getActions() != null
            && access.getActions().contains(action) && scopeType.equalsIgnoreCase(access.getSetFilterType());
    }

    /**
     * 数据权限查找判断逻辑接口
     *
     * @param <T>
     */
    interface DataAccessPredicate<T extends DataAccess> extends Predicate<DataAccess> {

        @Override
        boolean test(DataAccess access);

        @Override
        default DataAccessPredicate<T> and(Predicate<? super DataAccess> other) {
            return (t) -> test(t) && other.test(t);
        }

        @Override
        default DataAccessPredicate<T> or(Predicate<? super DataAccess> other) {
            return (t) -> test(t) || other.test(t);
        }
    }

}
